
#ifndef __RSP_S_CONNECTOR__HEADER_FILE__
#define __RSP_S_CONNECTOR__HEADER_FILE__

#include "coid/comm/dynarray.h"
#include "coid/comm/str.h"
#include "coid/comm/binstream/binstream.h"
#include "coid/comm/binstream/circularstreambuf.h"

#include "s_controller.h"
#include "s_update.h"
#include "s_object.h"
#include "s_objlist.h"


typedef rsp_view_container<rsp_s_object_state>  t_viewcont;


////////////////////////////////////////////////////////////////////////////////
///Server side object rsp_s_object manager
struct rsp_s_connector
{
    uint _ackstateid;

    rsp_s_connector( t_viewcont& vc, uint inroomid, const token& nick ) : _vc(vc)
    {
        _inroomid = inroomid;
        _nick = nick;
        
        reset();
    }

    void reset()
    {
        _cstate = CS_INITIAL;
        _ackstateid = RSP_SETTINGS.active_frame() - RSP_SETTINGS._ntrailing - 1;

        _list.reset();
        _ssid = 0;
    }


    void view_add( rsp_base* p )
    {
        rsp_s_object_state* os = _vc.view_find(p);
        DASSERT(os);

        _list.ref_ins(os);
    }

    void view_del( rsp_base* p )
    {
        //rsp_s_object_state* os = _vc.view_find(p);
        //DASSERT(os);

        _list.ref_del( p->oid() );
    }

    void set_owned_object( rsp_base* p )
    {
        rsp_s_object_state* os = _vc.view_find(p);
        DASSERT(os);

        _list.set_owned_object(os);
    }


    bool verify_oid( rsp_base* p )
    {
        return 0 != _list.ref_find( p->oid() );
    }

    void init_ssid( uint32 ssid )               { _ssid = ssid; }
    uint32 get_ssid() const                     { return _ssid; }

    const charstr& get_nick() const             { return _nick; }

    uint room_id() const                        { return _inroomid; }

    
    bool is_dead() const
    {
        //give some more time to the newly connected clients
        if( _cstate < CS_INCREMENTAL )
            return int(RSP_SETTINGS.active_frame() - _ackstateid) > (int)RSPS_SETTINGS._nkickoffstart_frames;
        else
            return int(RSP_SETTINGS.active_frame() - _ackstateid) > (int)RSPS_SETTINGS._nkickoff_frames;
    }

    ///Generate the differential update into the binstream
    /**
        This method is called each network replication frame, and generates an update packet for
        the client.

        The update packet contains commands and diffs for each managed object. Normally server sends
        only the commands, but if some object's field was changed by another means than by a command,
        or indirectly by a command of another object that isn't in the replicated set, server sends
        the changed fields. The changes should be applied in the reference state. Otherwise, server
        sends all commands from last acknowledged state up to the last known wait state.

        Server sends message when refstate changes.
    **/
    bool diff( binstream* bot )
    {
        if( _cstate == CS_INITIAL )
            _cstate = CS_UNACKED;

        uint stateid = _cstate == CS_UNACKED
            ? RSP_SETTINGS.active_frame()
            : _ackstateid;

        rsp_s_update upd( this, bot, stateid );
        if( upd.full_update() )
        {
            stateid = RSP_SETTINGS.active_frame();
            LOG(2) {
                *TLOG << timefmt(RSP_SETTINGS.active_frame()) << " switching back to full updates,  ackstate: "
                    << timefmt(_ackstateid) << ", delta: " << (RSP_SETTINGS.active_frame()-_ackstateid) << BINSTREAM_FLUSH;
            }
        }

        //push current refstateid and acknowledged state id
        (*bot) << (uint8)RSP_PACKET::DIFF << _Timer.time()
            << (ushort)RSP_SETTINGS.active_frame() << (ushort)stateid;

        return _list.diff(upd);       //replicate the primary object list
    }


    ///Apply commands from client
    /// @return true if the message was read successfully
    int patch( binstream& bin )
    {
        ushort stid;
        bin >> stid;    //acknowledged state (server state received by the client)

        short d = stid - (ushort)_ackstateid;
        if( d < 0 )
        {
            LOG(1) {
                *TLOG << timefmt(RSP_SETTINGS.active_frame()) << " AMBIGUOUS patch for "
                    << timefmt(_ackstateid) << " (" << d << ")" << BINSTREAM_FLUSH;
            }
            return 0;
        }

        if( _ackstateid+d > RSP_SETTINGS.active_frame() ) {
            LOG(1) {
                *TLOG << timefmt(RSP_SETTINGS.active_frame()) << " FUTURE ack for "
                    << timefmt(_ackstateid+d) << " +"
                    << (_ackstateid+d-RSP_SETTINGS.active_frame()) << BINSTREAM_FLUSH;
            }
            return 0;
        }

        _ackstateid += d;

        if( _cstate != CS_INCREMENTAL )
            LOG(2) { *TLOG << timefmt(RSP_SETTINGS.active_frame()) << " ack message for " << timefmt(_ackstateid) << BINSTREAM_FLUSH; }

        _cstate = CS_INCREMENTAL;
        _list.set_incremental_mode();

        uchar ncmd;
        bin >> ncmd;

        if(!ncmd) {
            bin.acknowledge();
            return -1;//1;
        }

        rsp_s_object_state* sos = _list.get_owned_object();
        if(!sos)
        {
            LOG(1) { *TLOG << "message for unowned object ignored\n"; }
            return 0;
        }

        return sos->read_cmd( ncmd, _ackstateid, bin );
    }


protected:

    uint32 _ssid;                           ///< session id
    int  _cstate;                           ///< connection state, see enum
    enum {
        CS_INITIAL              = -1,
        CS_UNACKED              = 0,
        CS_INCREMENTAL          = 1,
    };


    rsp_s_objlist _list;                    ///< root object list

    uint _inroomid;
    rsp_view_container<rsp_s_object_state>& _vc;

    charstr _nick;
};



#endif //__RSP_S_CONNECTOR__HEADER_FILE__
